#include "qemu/osdep.h"
#include "qemu/module.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "qapi/visitor.h"
#include "trace.h"
#include "chardev/char-fe.h"
#include "qemu/error-report.h"
#include "hw/misc/utils.h"

#define MAX_REG 100
#define MAX_OFFSET 4 * 2
#define TYPE_LM75_I2CSLAVE "lm75-i2c-slave"
#define LM75_I2CSLAVE(obj) OBJECT_CHECK(slave_state, (obj), TYPE_LM75_I2CSLAVE)
#define I2C_PATH "./output/data/mockdata/i2c/"

typedef struct
{
	I2CSlave parent_obj;
	CharBackend chrbe;
	uint8_t offset_width;
	uint8_t offset_bytes;
	char offset[MAX_OFFSET + 3];
	char *path;
	bool transfer_start;
	bool transfer_recv;
	uint8_t reg_id;
	cJSON *buffer;
	uint8_t tmp[MAX_REG];
	uint8_t tmp_size;
	bool tmp_save_flag; // 缓存是否需要修改的标志
	bool tmp_load_flag; // 缓存是否需要修改的标志
	time_t last_modify;
	struct stat file_stat;
} slave_state;

static uint8_t hex_to_uint8(const char *hex_str)
{
	char *endptr;
	long int result;

	errno = 0;
	result = strtol(hex_str, &endptr, 0);
	if (errno != 0 || endptr == hex_str || *endptr != '\0' || result < 0 || result > UINT8_MAX)
	{
		fprintf(stderr, "Invalid number format or out of range");
		return 0;
	}

	return (uint8_t)result;
}

static void clear_tmp(slave_state *ss)
{
	memset(ss->tmp, 0xFF, MAX_REG);
	ss->tmp_size = 0;
}

static void load_buffer(slave_state *ss)
{
	clear_tmp(ss);
	cJSON *regs = cJSON_GetObjectItemCaseSensitive(ss->buffer, ss->offset);
	int reg_length = cJSON_GetArraySize(regs);
	for (int i = 0; i < reg_length; i++)
	{
		cJSON *item = cJSON_GetArrayItem(regs, i);
		if (item)
		{
			ss->tmp[ss->tmp_size] = hex_to_uint8(item->valuestring);
			ss->tmp_size++;
		}
		else
		{
			warn_report("Invaild arryay index: %d\n", i);
		}
	}
	ss->tmp_size = 0;
}

static void save_buffer(slave_state *ss)
{
	cJSON *regs = cJSON_GetObjectItemCaseSensitive(ss->buffer, ss->offset);
	// int reg_length = cJSON_GetArraySize(regs);
	char mid[5] = "0x";
	for (int i = 0; i < ss->tmp_size; i++)
	{
		cJSON *item = cJSON_GetArrayItem(regs, i);
		if (item)
		{
			sprintf(mid + 2, "%02X", ss->tmp[i]);
			cJSON_SetValuestring(item, mid);
		}
		else
		{
			warn_report("Invaild arryay index: %d\n", i);
		}
	}
}

static int lm75_i2cslave_event(I2CSlave *i2c, enum i2c_event event)
{
	slave_state *s = LM75_I2CSLAVE(i2c);

	switch (event)
	{
		/* 数据传输开始，打开文件 */
	case I2C_START_SEND:
		s->tmp_save_flag = true;
		s->tmp_load_flag = true;
		s->transfer_start = true;
		s->transfer_recv = false;
		s->offset_bytes = 0;
		strcpy(s->offset, "0x");
		clear_tmp(s);
		if (stat(s->path, &s->file_stat) != 0)
		{
			error_report("Get timestamp from %s failed;\n", s->path);
		}
		else if (s->file_stat.st_mtime != s->last_modify)
		{
			warn_report("Detect %s changed, Reload buffer;\n", s->path);
			free(s->buffer);
			s->buffer = read_json(s->path);
			s->last_modify = s->file_stat.st_mtime;
		}
		break;
	case I2C_START_RECV:
		s->tmp_save_flag = false;
		s->tmp_load_flag = true;
		s->transfer_start = true;
		s->transfer_recv = true;
		break;
		/* 数据传输结束，关闭文件 */
	case I2C_FINISH:
		s->transfer_start = false;
		if (s->tmp_save_flag)
		{
			save_buffer(s);
			write_json(s->path, s->buffer);
		}
		break;
	case I2C_NACK:
		break;
	}
	return 0;
}

static uint8_t lm75_i2cslave_rx(I2CSlave *i2c)
{
	slave_state *s = LM75_I2CSLAVE(i2c);
	if (s->tmp_load_flag)
	{
		load_buffer(s);
		s->tmp_load_flag = false;
	}
	int rx = s->tmp[s->tmp_size];
	s->tmp_size++;

	return rx;
}

static int lm75_i2cslave_tx(I2CSlave *i2c, uint8_t data)
{
	/* 前两个参数计算偏移量，后面的参数为要写入的数据 */
	slave_state *s = LM75_I2CSLAVE(i2c);
	if (!s->transfer_recv)
	{
		sprintf(s->offset + (s->offset_bytes + 1) * 2, "%02X", data);
		s->offset_bytes++;
		if (s->offset_bytes == s->offset_width)
		{
			s->transfer_recv = true;
		}
	}
	else
	{
		s->tmp[s->tmp_size] = data;
		s->tmp_size++;
	}
	return 0;
}

static void lm75_i2cslave_reset(DeviceState *dev)
{
	slave_state *s = LM75_I2CSLAVE(dev);
	s->transfer_start = false;
}

static void lm75_i2cslave_realize(DeviceState *dev, Error **errp)
{
	slave_state *s = LM75_I2CSLAVE(dev);
	s->transfer_recv = false;
	s->offset_width = 1;
	s->tmp_size = 0;
	s->path = g_strdup_printf("%s%d/0x90_lm75.json", I2C_PATH, s->reg_id);
	s->buffer = read_json(s->path);
	if (stat(s->path, &s->file_stat) != 0)
	{
		error_report("Get timestamp from %s failed;\n", s->path);
		return;
	}
	s->last_modify = s->file_stat.st_mtime;
}

static void generic_i2cslave_access_addr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
	uint8_t *ptr = (uint8_t *)object_field_prop_ptr(obj, (Property *)opaque);
	visit_type_uint8(v, name, ptr, errp);
}

static const PropertyInfo generic_i2cslave_prop_regaddr = {
	.name = "uint8",
	.get = generic_i2cslave_access_addr,
	.set = generic_i2cslave_access_addr,
	// .realized_set_allowed = true,
};

static Property lm75_i2cslave_properties[] = {
	DEFINE_PROP_UNSIGNED_NODEFAULT("reg-id", slave_state, reg_id, generic_i2cslave_prop_regaddr, uint8_t),
	DEFINE_PROP_END_OF_LIST(),
};

static void lm75_i2cslave_class_init(ObjectClass *klass, void *data)
{
	DeviceClass *dc = DEVICE_CLASS(klass);
	I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
	k->event = lm75_i2cslave_event;
	k->recv = lm75_i2cslave_rx;
	k->send = lm75_i2cslave_tx;
	dc->reset = lm75_i2cslave_reset;
	dc->realize = lm75_i2cslave_realize;
	device_class_set_props(dc, lm75_i2cslave_properties);
}

static void lm75_i2cslave_init(Object *obj)
{
	slave_state *s = LM75_I2CSLAVE(obj);
	s->transfer_start = false;
}

static const TypeInfo lm75_i2cslave_info = {
	.name = TYPE_LM75_I2CSLAVE,
	.parent = TYPE_I2C_SLAVE,
	.instance_size = sizeof(slave_state),
	.class_init = lm75_i2cslave_class_init,
	.instance_init = lm75_i2cslave_init,
};

static void lm75_i2cslave_register_types(void)
{
	type_register_static(&lm75_i2cslave_info);
}

type_init(lm75_i2cslave_register_types)